package com.example.demo.config;

import cn.hutool.core.date.DateException;
import com.baomidou.mybatisplus.extension.api.IErrorCode;
import com.baomidou.mybatisplus.extension.api.R;
import com.baomidou.mybatisplus.extension.enums.ApiErrorCode;
import com.baomidou.mybatisplus.extension.exceptions.ApiException;
import com.example.demo.ErrorCode;
import com.example.demo.common.excel.ExcelException;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.ShiroException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindingResult;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * <p>
 * 全局异常处理
 * </p>
 *
 * @author zhaiding
 * @since 2019-5-1
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(value = ExcelException.class)
    @ResponseStatus(value = HttpStatus.NOT_IMPLEMENTED)
    public R excelException(ExcelException e) {
        log.info("ExcelException : " + e.getMessage());
        return R.failed(e.getMessage());
    }

    @ExceptionHandler(value = UnauthenticatedException.class)
    @ResponseStatus(value = HttpStatus.UNAUTHORIZED)
    public R authenticatedException(UnauthenticatedException e) {
        log.info("未登录 : " + e.getMessage());
        return R.failed(e.getMessage());
    }

    @ExceptionHandler(value = UnauthorizedException.class)
    @ResponseStatus(value = HttpStatus.FORBIDDEN)
    public R authorizedException(UnauthorizedException e) {
        return R.failed(e.getMessage());
    }

    /**
     * @ RequiresAuthentication 如果用户未登录，抛出异常 UnauthenticatedException
     * @ RequiresRoles 如果用户角色不符合，抛出异常 UnauthorizedException
     * @ RequiresPermissions 如果用户权限不符合，抛出异常 UnauthorizedException
     */
    @ExceptionHandler(value = ShiroException.class)
    @ResponseStatus(value = HttpStatus.FORBIDDEN)
    public R shiroException(ShiroException e) {
        // 密码错误
        if (e instanceof IncorrectCredentialsException) {
            return R.failed("密码错误");
        }
        // 找不到用户
        if (e instanceof UnknownAccountException) {
            return R.failed(e.getMessage());
        }
        return R.failed(e.getMessage());
    }


    /**
     * <p>
     * 500 系统内部异常
     * <p>
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
    public R<Object> globalException(Exception e) {
        log.error("Error: globalException StackTrace : {}", e.getMessage(), e);
        return R.failed(ApiErrorCode.FAILED);
    }

    /**
     * <p>
     * 自定义 REST 业务异常
     * 501 不可执行
     * <p>
     *
     * @param e 异常类型
     */
    @ExceptionHandler(value = ApiException.class)
    @ResponseStatus(value = HttpStatus.NOT_IMPLEMENTED)
    public R<String> handleBadRequest(ApiException e) {
        IErrorCode errorCode = e.getErrorCode();
        if (null != errorCode) {
            log.debug("Rest request error, {}", errorCode.toString());
            return R.failed(errorCode);
        }
        log.debug("Rest request error, {}", e.getMessage());
        return R.failed(e.getMessage());
    }

    /**
     * spring 断言
     * 501 不可执行
     */
    @ExceptionHandler(value = IllegalArgumentException.class)
    @ResponseStatus(value = HttpStatus.NOT_IMPLEMENTED)
    public R<String> illegalArgumentException(IllegalArgumentException e) {
        log.debug("Rest request error, {}", e.getMessage());
        return R.failed(e.getMessage());
    }

    /**
     * 404 处理
     */
    @ExceptionHandler(value = NoHandlerFoundException.class)
    @ResponseStatus(value = HttpStatus.NOT_FOUND)
    public R<String> httpRequestMethodNotSupportedException(NoHandlerFoundException e) {
        return R.failed(e.getMessage());
    }

    /**
     * 请求方式成为
     * 405 方法不允许
     */
    @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
    @ResponseStatus(value = HttpStatus.METHOD_NOT_ALLOWED)
    public R<String> httpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        return R.failed(e.getMessage());
    }

    /**
     * 时间转换失败
     */
    @ExceptionHandler(value = DateException.class)
    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    public R dateException(DateException e) {
        return R.failed(e.getMessage());
    }

    /**
     * 缺少参数
     */
    @ExceptionHandler(value = MissingServletRequestParameterException.class)
    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    public R missingServletRequestParameterException(MissingServletRequestParameterException e) {
        return R.failed(e.getMessage());
    }

    /**
     * <p>
     * 消息反序列化异常
     * 400 错误请求
     * </p>
     *
     * @param e 异常类型
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    public R<String> httpMessageNotReadableException(HttpMessageNotReadableException e) {
        return R.failed(ErrorCode.DESERIALIZE);
    }

    /**
     * <p>
     * 参数校验异常
     * 400 错误请求
     * </p>
     *
     * @param e 异常类型
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    public R<Object> validated(MethodArgumentNotValidException e) {
        BindingResult bindingResult = e.getBindingResult();
        if (bindingResult.hasErrors()) {
            List<Object> jsonList = new ArrayList<>();
            bindingResult.getFieldErrors().forEach(fieldError -> {
                Map<String, Object> jsonObject = new HashMap<>(2);
                jsonObject.put("name", fieldError.getField());
                jsonObject.put("msg", fieldError.getDefaultMessage());
                jsonList.add(jsonObject);
            });
            return R.restResult(jsonList, ErrorCode.PARAM);
        }
        return R.failed(ApiErrorCode.FAILED);
    }
}
